package com.suxin.classloader;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author Tang
 * @classname ClassLoader1
 * @description [ https://www.toutiao.com/i6852925949529293324 ]
 * @date 2021/7/19 15:27
 */
public interface HelloService {

    String sayHello();

    BlockingQueue<String> CLASSES = new LinkedBlockingDeque<>(10);
    BlockingQueue<String> EVENTS = new LinkedBlockingDeque<>(10);

    AtomicBoolean START = new AtomicBoolean(false);

    static void main(String[] args) throws Exception{
        Thread thread = new Thread(() -> {
            ClassLoader classLoader = new ClassLoader() {
                @Override
                protected Class<?> findClass(String className) throws ClassNotFoundException {
                    String path1 = "G:\\suxin";
                    String clzName = className.substring(className.lastIndexOf(".") + 1);
                    try {
                        Optional<Path> firstClass = Files.find(Paths.get(path1), 1, (path, basic) -> {
                            return path.getFileName().toString().endsWith(clzName + ".class");
                        }, FileVisitOption.FOLLOW_LINKS).findFirst();
                        byte[] cls = firstClass.map(path -> {
                            try (ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(1024)) {
                                Files.copy(path, byteOutputStream);
                                return byteOutputStream.toByteArray();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                            throw new RuntimeException("not found class!");
                        }).get();
                        EVENTS.put(String.format("加载类成功,类名:%s", className));
                        return defineClass(className, cls, 0, cls.length);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return null;
                }
            };
            Thread x = new Thread(() -> {
                try {
                    if (START.compareAndSet(false, true)) {
                        Thread y = new Thread(() -> {
                            for (; ; ) {
                                try {
                                    String event = EVENTS.take();
                                    System.out.println("接收到事件,事件内容：" + event);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        },"Y");
                        y.setDaemon(true);
                        y.start();
                    }
                    for (; ; ) {
                        String take = CLASSES.take();
                        Class<?> aClass = classLoader.loadClass(take);
                        HelloService helloService = (HelloService) aClass.newInstance();
                        System.out.println(helloService.sayHello());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"X");
            x.setContextClassLoader(classLoader);
            x.setDaemon(true);
            x.start();
        }, "thread");

        thread.start();
        CLASSES.put("com.suxin.classloader.DefaultHelloService1");
        CLASSES.put("com.suxin.classloader.DefaultHelloService2");
        Thread.sleep(500);
        System.gc();
        Thread.sleep(500);
        System.gc();
        Thread.sleep(Long.MAX_VALUE);
    }

}